home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Technotools
/
Technotools (Chestnut CD-ROM)(1993).ISO
/
misc_pto
/
mcbtrc2
/
trace.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-11-26
|
11KB
|
385 lines
/*
* Antoun Kanawati
* November 25, 1989
*
* Another DOS hacking program.
*
* A program to trace DOS's MCB chain.
*
* A Memory Control Block (MCB) is an undocumented DOS data structure built as
* follows:
* size = 16 bytes.
* offset contents
* 0 byte 'M' ==> middle of chain.
* 'Z' ==> end of chain.
* 1 word 0 ==> Free.
* != 0 ==> Segment address of owner (PSP address).
* 3 word ==> Size of block.
* 4 11 bytes ==> reserved or unused.
*
* For each block that DOS allocates thru function 0x48, one of these blocks
* is created, an it is just before the block whose address is returned.
* (This is true even for blocks of size 0.)
*
* Furthermore, all MCB's are aligned on paragraph boundaries.
*
* The program first sets DOS's strategy to ``first fit'', and then requests a
* block of size 0 from DOS. This insures that we get the lowest possible
* paragraph address.
*
* Then, the guessing starts:
* We look backwards for an MCB that lies before the one just allocated,
* and we keep looking backwards until the end of chain (avoiding the
* BIOS data area).
* Once we have an estimate of the start point, it is a simple matter to
* trace the chain forward.
*
* The program name is printed by looking in the owner's PSP, and scanning the
* owner's environment block (if it is still attached to the program).
*
* For details on how the environment is located, look at ADVANCED MS-DOS, or
* the MS-DOS encyclopedia.
*
* The MCB structure is sketched in a few non-official references. Official
* MS-DOS references (such as the two above) barely mention it.
* None of the references I have seen mention how to find the starting MCB,
* so I had to write an educated guessing routine to do it.
*
* There might be a better method to find the starting MCB than the one
* presented here. Unfortunately, I am not aware of it.
*
* A warning:
* This program sets DOS's allocation strategy to ``first fit''. Since there
* are two other strategies, this means we could be changing something. If
* you intend to use the techniques presented here in an environment where
* allocation strategies matter, you should restore the strategy to its
* original value before terminating the program.
*
* A comment on the use of the program:
* The casual user and the high level programmer will find this stuff of no
* use. This is intended for those who like to know what happens for the
* sake of knowing what happens (i.e.: for the fun of it). There are
* programs of a higher quality output and usefulness available to list all
* your memory resident programs, the interrupts they use, etc....
* These other niceties are trivial problems once we have traced the chain:
* sort the blocks by segment address, and scan the vector table checking the
* segment addresses against the owners of MCB's.
*
* Disclaimer:
* You may use this program at your own risk. The author is not responsible
* in any manner for damages incurred by the use of the program, nor for
* benefits gained from the source code. That is: feel free to copy, modify,
* and distribute the source code, but do not use it blindly -- This is
* intended for competent programmers with substantial DOS knowledge. And
* please keep the source code of this program with to its binaries -- The
* executable is quite worthless without the source code.
*
* A final hacker's note:
* You will notice on the output a free block of size zero. This is the
* block used to start the search backwards for the starting MCB. You will
* also find two blocks bearing this program's name: one is for the
* environment, and the other is for the program.
*
* You need not worry about fragmentation: DOS compacts free memory whenever
* necessary, and you can see that by modifying this program to trace the
* allocation chain between allocations and releases of blocks of various
* sizes.
*
* And if you are interested in seeing how allocation strategies affect DOS's
* allocation behavior, then you can use parts of this program to trace the
* allocation chain between allocations and releases to see where new data is
* allocated.
*
* Interesting experiments:
* trace
* command/c trace
* command/c command/c trace
*
*/
#include <stdio.h>
#if sizeof(char *) != 4
/*
* The program must operate with 32-bit pointers.
*
*/
main()
{
printf("This program must be compiled in a large data pointer model.\n");
exit(0);
}
#else
/* Going from segment addresses to normalized pointer values. */
#define seg_to_charp(x) ((char *) (((long) (x)) << 16))
/* MCB access protocols: */
#define MCB_owner(ptr) (*((unsigned *) (((char *) (ptr))+1)))
#define MCB_size(ptr) (*((unsigned *) (((char *) (ptr))+3)))
#define BIOS_END 0x800
/*
* DOS call to set first-fit strategy.
*
*/
void first_fit()
{
asm mov ah, 0x58;
asm mov al, 0x01;
asm mov bx, 0x00;
asm int 0x21;
}
/*
* DOS call to get a block.
*
*/
unsigned get_block(unsigned n)
{
asm mov ah, 0x48;
asm mov bx, n;
asm int 0x21;
asm jc err;
asm jmp ok;
err:
asm xor ax,ax;
ok:
return _AX;
}
/*
* DOS call to release a block gotten with the above procedure.
*
*/
void release_block(unsigned n)
{
unsigned err_code;
asm push es;
asm mov ah, 0x49;
asm mov es, n;
asm int 0x21;
asm pop es;
asm jc err;
asm jmp done;
err_code = _AX;
err:
printf("Error code = %04X\n", err_code);
done:
return;
}
/*
* Environment scanner:
*
* Given the PSP of the owner checks:
* 1- PSP starts with a INT 20H instruction.
* 2- Retrieves the environment block address, and checks that it is
* attached to the program.
* Then, the evironment is scanned for the program name, which is printed.
*
*/
void do_env(unsigned psp)
{
char *envp;
char *psp_ptr;
unsigned envseg;
unsigned envsize;
psp_ptr = seg_to_charp(psp);
/* If the PSP does not start with INT 20H, return. */
if ((*((unsigned *) psp_ptr)) != 0x20CD) /* INT 20H */
{
printf("\n");
return;
}
/* Look at the MCB of environment block : */
envseg = *((unsigned *) (psp_ptr + 0x2C));
envp = seg_to_charp(envseg-1);
/* The MCB must be an M-type, owned by PSP that was used to reach it, */
/* and must have a non-zero size. */
/* If any of these conditions fails, we leave. */
if ( *envp != 'M' || MCB_owner(envp) != psp || MCB_size(envp) == 0)
{
printf("\n");
return;
}
envsize = MCB_size(envp) << 4; /* paragraphs --> bytes. */
/* Now, we skip the environment strings, which leaves us at the program */
/* name. */
envp = seg_to_charp(envseg);
while (*envp && envsize)
while (*envp++ && envsize--)
;
envp += 3;
/* If the whole environment was skipped, we print nothing. */
if (envsize)
printf("%s\n", envp);
else
printf("\n");
}
/*
* Prints contents of MCB:
* 1- Check for valid format.
* 2- Pick up the data on size an owner.
* 3- Print.
* 4- Try to find an owner's name.
*
*/
void dump_mcb(unsigned seg_ref)
{
char *p;
unsigned owner;
unsigned size;
p = seg_to_charp(seg_ref);
if (*p != 'M' && *p != 'Z')
{
printf("Bad MCB, first byte == %02X\n", (unsigned char) (*p));
return;
}
owner = MCB_owner(p);
size = MCB_size(p);
printf("%04X %02X %04X %04X ",
seg_ref,
(unsigned char) (*p),
owner,
size);
if (*p == 'Z')
printf("End of chain.\n");
else if (owner == 0)
printf("Free fragment.\n");
else
do_env(owner);
}
/*
* Forward tracing in the allocation chain.
*
* Note that we assume that we have a proper MCB address at start.
*
*/
void trace_chain(unsigned mcb_seg)
{
unsigned seg;
char *p;
seg = mcb_seg;
printf("Addr LBL OWNR SIZE\n");
printf("---- ---- ---- ----\n");
for(;;)
{
dump_mcb(seg); /* Print the current MCB. */
p = seg_to_charp(seg);
if (*p == 'Z') break; /* End of chain ? */
if (*p != 'M') /* Check for errors. */
{
printf("Unknown designator : %02X\n", (unsigned char) *p);
break;
}
seg = seg + MCB_size(p) + 1; /* Move to next MCB */
}
printf("\n"); /* Finish table. */
}
/*
* Guessing algorithm: This produces an MCB address for what might be the
* first MCB in the chain using the following criteria:
* 1- The MCB in question must lead to the current MCB.
* 2- It must be located above the BIOS data area.
* 3- It is free, or owned by a process whose PSP is above paragraph address
* 0x800 (above the BIOS data area).
*
* We start with an initial address (i-1), and keep trying backwards until
* we hit BIOS data area. The last guess that passed the test is taken as
* the start of the chain.
*
* Note that this is not 100% guaranteed to be correct, but the chances of
* finding a bogus MCB are low enough to make it usable.
*
*/
unsigned find_start(unsigned i)
{
unsigned a, b;
char *p;
for(a = i, b = i-1; b >= BIOS_END; )
{
p = seg_to_charp(b);
if (*p == 'M' && MCB_size(p) + b + 1 == a &&
(MCB_owner(p) >= BIOS_END || MCB_owner(p) == 0))
a = b;
b--;
}
return a;
}
/*
* And finally, the main() procedure.
*
*/
main()
{
unsigned m;
unsigned start;
first_fit(); /* set strategy to start at lowest address. */
m = get_block(0); /* Get lowest free address. */
if (m == 0)
{
printf("Allocation failed.\n");
exit(0);
}
start = find_start(m-1); /* Find a start MCB. */
release_block(m); /* get rid of the MCB used to start. */
trace_chain(start);
exit(0); /* A clean exit. */
}
#endif